#include <iostream>
#include <unistd.h>
#include <ctime>
#include <cstdlib>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"

using namespace std;

enum
{
    UsageError = 1,
    ArgError,
    PipeError
};
void Usage(const string &proc)
{
    cout << "Usage: " << proc << " subprocess-num" << endl; 
}

class Channel
{
public:
    Channel(int wfd,pid_t sub_process_id,const string &name)
        : _wfd(wfd)
        , _sub_process_id(sub_process_id)
        , _name(name)
    {}
    void PrintDebug()
    {
        cout << "_wfd: " << _wfd;
        cout << ",_sub_process_id: " << _sub_process_id;
        cout << ",_name: " << _name;
    }
    string name()
    {
        return _name;
    }
    int wfd()
    {
        return _wfd;
    }
    pid_t pid()
    {
        return _sub_process_id;
    }
    void Close()
    {
        close(_wfd);
    }
    ~Channel() {}
private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;
};
class ProcessPool
{
public:
    ProcessPool(int sub_process_num) : _sub_process_num(sub_process_num)
    {}
    int CreateProcess(work_t work)
    {
        vector<int> fds;
        for(int number = 0;number < _sub_process_num;number++)
        {
            int pipefd[2]{0};
            int n = pipe(pipefd);
            if(n < 0)
                return PipeError;
            pid_t id = fork();
            if(id == 0)
            {
                if(!fds.empty())
                {
                    cout << "close w fd: ";
                    for(auto fd : fds)
                    {
                        close(fd);
                        cout << fd << " ";
                    }
                    cout << endl;
                }
                close(pipefd[1]);
                dup2(pipefd[0],0);
                work(pipefd[0]);
                exit(0);
            }
            string cname = "channel-" + to_string(number);
            // father
            close(pipefd[0]);
            channels.push_back(Channel(pipefd[1],id,cname));

            // 保存父进程wfd
            fds.push_back(pipefd[1]);
        }
        return 0;
    }
    int NextChannel()
    {
        static int next = 0;
        int c = next;
        next++;
        next %= channels.size();
        return c;
    }
    void SendTaskCode(int index,uint32_t code)
    {
        cout << "send code: " << code << "to " << channels[index].name() << " sub process id: " << channels[index].pid() << endl;
        write(channels[index].wfd(),&code,sizeof(code));
    }
    void KillAll()
    {
        for(auto &channel : channels)
        {
            channel.Close();
            pid_t pid = channel.pid();
            
            pid_t rid = waitpid(pid, nullptr, 0);
            if(rid == pid)
            {
                cout << "wait sub process: " << pid << " success..." << endl;
            }
            cout << channel.name() << " close done" << " sub process quit now : " << channel.pid() << endl;
        }
    }
    void wait()
    {

    }
    void Debug()
    {
        for(auto &channel : channels)
        {
            channel.PrintDebug();
        }
    }

    ~ProcessPool() {}

private:
    int _sub_process_num;
    vector<Channel> channels;
};

void CtrlProcessPool(ProcessPool *ProcessPool_ptr,int cnt)
{
    while(cnt)
    {
        // a.选择一个进程和通道
        int channel = ProcessPool_ptr->NextChannel();

        // b.选择一个服务
        uint32_t code = NextTask();

        // c.发送任务
        ProcessPool_ptr->SendTaskCode(channel,code);

        sleep(1);
        cnt--;
    }
}

int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int sub_process_num = stoi(argv[1]);
    if(sub_process_num <= 0)
        return ArgError;

    srand((uint64_t)time(nullptr));

    // 1.创建通信信道和子进程 
    ProcessPool *processpool_ptr = new ProcessPool(sub_process_num);
    processpool_ptr->CreateProcess(worker);
    processpool_ptr->Debug();

    // 2.控制子进程
    CtrlProcessPool(processpool_ptr,10);
    cout << "task run done" << endl;

    // 3.回收子进程
    processpool_ptr->KillAll();
    processpool_ptr->wait();

    delete processpool_ptr;
    return 0;
}